package it.unina.lab.config;

import java.util.ArrayList;

import org.apache.log4j.Logger;

import com.continuent.tungsten.commons.patterns.fsm.Entity;
import com.continuent.tungsten.commons.patterns.fsm.EntityAdapter;
import com.continuent.tungsten.commons.patterns.fsm.FiniteStateException;
import com.continuent.tungsten.commons.patterns.fsm.NegationGuard;
import com.continuent.tungsten.commons.patterns.fsm.PositiveGuard;
import com.continuent.tungsten.commons.patterns.fsm.State;
import com.continuent.tungsten.commons.patterns.fsm.StateMachine;
import com.continuent.tungsten.commons.patterns.fsm.StateTransitionMap;
import com.continuent.tungsten.commons.patterns.fsm.StateType;

import diuf.diva.hephaistk.xml.smuiml.Context;
import diuf.diva.hephaistk.xml.smuiml.Result;
import diuf.diva.hephaistk.xml.smuiml.Transition;
import diuf.diva.hephaistk.xml.smuiml.Trigger;


public class StateMachineBuilder {

	private Logger logger = Logger.getLogger(StateMachineBuilder.class);
	private StateTransitionMap mappaTransizione;
	private ArrayList<Context> contexts;
	private Context startContext;
	private Entity entity;
	
	
	/* Il primo context trovato viene definito come lo stato di START della macchina */
	public StateMachineBuilder(Entity entity, ArrayList<Context> contexts){
		this.contexts = contexts;
		this.entity = entity;
		mappaTransizione = new StateTransitionMap();
		try {
			startContext = contexts.get(0);
			logger.info("Creazione stato di START della macchina: " + startContext.getAttName().getValue());
			mappaTransizione.addState(new State(startContext.getAttName().getValue(), StateType.START, null, null));
			contexts.remove(0);
		} catch (FiniteStateException e) {
			logger.error("Errore durante la creazione dello stato start.");
			logger.error(e);
		}
	}
	
	
	
	/* Per ogni altro context viene creato uno stato attivo all'interno della macchina */
	public void processContexts(){
		for(Context con : contexts){
			try {
				logger.info("creazione stato ACTIVE: " + con.getAttName().getValue());
				mappaTransizione.addState(new State(con.getAttName().getValue(), StateType.ACTIVE, null, null));
			} catch (FiniteStateException e) {
				logger.error("Errore durante il processamento dei contesti.");
				logger.error(e);
			}		
		}
		logger.info("Fine creazione stati della macchina.");
	}
	
	
	
	public void processTransitions(){
		processTransitions(startContext);
		for(Context con : contexts)
			processTransitions(con);
		
		logger.info("Fine creazione transazioni della macchina.");
		
	}
	
	

	private void processTransitions(Context cont) {
		ArrayList<Transition> transazioni = new ArrayList<Transition>();
		transazioni.addAll(cont.getTransitions());
		
			//per ogni transazione prendiamo il result e gestiamo i suoi triggers
			for(Transition tran : transazioni){
				Result result = tran.getResult(0);
				String action_value = result.getAttAction().getValue();
			   
				//stiamo analizzando sempre il caso PAR_OR
				//TODO analizzare anche il caso PAR_AND
				ArrayList<Trigger> triggers = new ArrayList<Trigger>(tran.getFusionElement(0).getTriggers());
				try {
					logger.info("Creazione transazione da: " + cont.getAttName().getValue() + " a " + result.getAttContext().getValue());
					logger.info("Triggers: " + triggers);
					mappaTransizione.addTransition(tran.getName(), new TriggerParOrGuard(triggers), mappaTransizione.getStateByName(cont.getAttName().getValue()), new ActionDispatcher(action_value), mappaTransizione.getStateByName(result.getAttContext().getValue()));
				} catch (FiniteStateException e) {
					logger.error("Errore durante il processamento dei triggers all'interno delle transazioni.");
					logger.error(e);
				}
				
			
			}
		
	}
	
	
	
	/* Inseriamo uno stato END nella macchina per correttezza della stessa
	 * ma questo non viene mai richiamato */
	public StateMachine endBuildProcess(){
		StateMachine macchina = null;
		try {
			mappaTransizione.addState(new State("END", StateType.END, null, null));
			mappaTransizione.addTransition("TO-END", new NegationGuard(new PositiveGuard()), mappaTransizione.getStateByName(startContext.getAttName().getValue()), null, mappaTransizione.getStateByName("END") );
			mappaTransizione.build();
			macchina = new StateMachine(mappaTransizione, new EntityAdapter(entity));
			logger.info("Fine creazione della macchina");
		} catch (FiniteStateException e) {
			logger.error("Errore durante il build della macchina");
			logger.error(e);
		}
		
		return macchina;
	}
}
